;主引导片区程序
;同时定义了FAT16文件系统
;使用了
%include "lib/global.inc"
SECTION MBR vstart=0x7c00
;0x0跳转指令
    jmp MBR_loader
    nop
;-------------------------------------------------------------------------------
;以下定义FAT16文件系统的主引导扇区参数。括号内数字含义：(文件内偏移)[十六进制]
	;生产厂商名     (03) 
    ;也可自行命名，实际影响不大
    BS_OEMName	    db	'GKOSBOOT'
	;每扇区字节数   (0B)
    BPB_BytesPerSec	dw	0x200
	;每簇扇区数     (0D)
    ;每簇扇区占8个扇区，即4k硬盘空间
    BPB_RsvdSecCnt	db	0x8
	;保留扇区数     (0E)
    ;FAT12保留扇区必须为1
    BPB_SecPerClus	dw	0x1
	;FAT表份数      (10)
    ;推荐设置为2
    BPB_NumFATs	    db	0x2
	;根目录可容纳的目录项数 (11)
    ;乘以32必须是每扇区字节数的偶数倍
    BPB_RootEntCnt	dw	224
    ;总扇区数       (13)
    ;仅用于十六位情况。
	BPB_TotSec16	dw	0
	;介质描述符     (15)
    ;不可移动，值为0xFB;可移动一般设0xF0
    ;可以设为0xF0或0xF8~0xFF
    ;必须在FAT[0]的低字节写入相同值
    BPB_Media	    db	0F0h
	;FAT表扇区数    (16)
    BPB_FATSz16	    dw	100
    ;每磁道扇区数   (18)
	BPB_SecPerTrk	dw	63
    ;磁头数         (1A)
	BPB_NumHeads	dw	16
    ;隐藏扇区数     (1C)
	BPB_hiddSec	    dd	0
    ;32位总扇区数   (20)
    ;若BPB_TotSec16为0，则由这个值记录扇区数
    ;对100M硬盘，其值为32000h
	BPB_TotSec32	dd	0x32000
    ;int13h的驱动器号(24)
	BS_DrvNum	    db	0
    ;未使用         (25)
	BS_Reserved1	db	0
	;扩展引导标记   (26)
    BS_BootSig	    db	29h
    ;卷序列号       (27)
	BS_VolID	    dd	0
	;卷标           (2B)
    ;在系统中显示的名称
    BS_VolLab	    db	'boot loader'
	;文件系统类型   (36)
    ;只是一个字符串，不被操作系统使用
    BS_FileSysType	db	'FAT16   '
;以下为引导代码，偏移量0x3E
;-------------------------------------------------------------------------------
MBR_loader:
    ;初始化段寄存器,均初始化为0
    mov ax, 0
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    
    ;初始化屏幕段
    mov ax, 0xb800
    mov gs, ax 

    ;清屏（设置显示模式为80*25 16色）
    mov ax, 3
    int 10h

    ;将loader.S载入虚拟机中(读入4个扇区)
    call FindLoader
    xor cx, cx
    mov cl, [BPB_RsvdSecCnt]
    mul cx
    add ax, FAT_SEC_OFFSET
    mov bx, LOADER_BASE_ADDR
    call ReadDisk16

    ;跳转执行loader.S
    jmp LOADER_CODE_ADDR


;-------------------------------------------------------------------------------
;name : ReadDisk16
;function : 在实模式读取一个扇区
;input :
;       ax —— 目标扇区的LBA编号
;       cl —— 读入的扇区数量
;       es:bx —— 目标内存起始地址
;output :
;       void
;-------------------------------------------------------------------------------
ReadDisk16:
    ;存住cx
    push cx
    ;扇区数cl = LBA % SPT + 1
    mov dx, word [BPB_SecPerTrk]
    div dl
    inc ah
    mov cl, ah
    ;磁头dh = (LBA / SPT) % HPC
    ;柱面ch = (LBA / SPT) / HPC 
    xor ah, ah  
    ;现在ax=LBA/SPT
    mov dx, word [BPB_NumHeads]
    div dl
    mov ch, al
    mov dh, ah
    ;al=读入的扇区数(刚压进来的cl)
    pop ax
    ;ah=2，功能号
    mov ah, 2
    ;设置驱动号为80h即硬盘的第一个分区
    mov dl, 80h
    ;调用int 13h/02h号中断， 读入磁盘信息
    int 13h
    ret

;-------------------------------------------------------------------------------
;name : FindLoader
;function : 在目录表中找到Loader.bin文件
;input :
;       void
;output :
;       ax -- Loader.bin目录项起始簇号

;-------------------------------------------------------------------------------
FindLoader:
    mov dx, FDT_SEC_ID
;最外层循环，遍历FDT的扇区
.FdtSecLoop:
    mov cl, 1
    mov ax, dx   
    mov bx, FDT_TEMP_ADDR
    call ReadDisk16
;中层循环，遍历一个扇区里的FDT表项
.FdtItemLoop:
    xor si, si
;内层循环，比较当前表项文件名是否为loader.bin
.FdtNameCmp:
    mov al, byte [LoaderName + si]
    cmp al, byte [bx + si]
    jne .NotMatched
    inc si
    cmp si, LOADER_NAME_SIZE
    jne .FdtNameCmp
;若匹配成功，直接返回
    mov ax, [bx + FDI_START_OFFSET]
    ret
;若匹配失败，进入下一个表项
.NotMatched:      
    add bx, FDT_ITEM_SIZE
    mov cx, [BPB_BytesPerSec]
    add cx, FDT_TEMP_ADDR
    cmp bx, cx
    jne .FdtItemLoop
    inc dx
    cmp dx, FDT_SEC_ID + FDT_SEC_SIZE
    jne .FdtSecLoop
    ;打印报错信息
    mov ah, 13h
    mov al, 1
    mov bh, 0
    mov bl, 0x07
    mov cx, [LoaderErrorLen]
    mov dx, 0
    mov bp, LoaderErrorMsg
    int 10h
    hlt
LOADER_NAME_SIZE    equ 11
LoaderName      db "LOADER  BIN"
LoaderErrorMsg  db "Can't find loader.bin"
LoaderErrorLen  dw $ - LoaderErrorMsg 
times 510-($-$$) db 0
db 0x55, 0xaa
